package cn.jane.rpc.api.spi;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * SPI 类加载器帮助类
 */
public class ServiceSupport {

    /**
     * 服务名称 与 服务实例集合 的映射
     */
    private final static Map<String, Collection<Object>> SERVICE_MAPPER = new HashMap<>();

    /**
     * 加载单个服务实例
     *
     * @param serviceClass 待加载服务实例的字节码对象
     * @param <S>          待加载服务实例的类型
     * @return 加载的服务实例
     */
    public static synchronized <S> S load(Class<S> serviceClass) {
        return load(serviceClass.getCanonicalName(), serviceClass);
    }

    /**
     * 加载单个服务实例
     *
     * @param serviceName  待加载服务实例的名称
     * @param serviceClass 待加载服务实例的字节码对象
     * @param <S>          待加载服务实例的类型
     * @return 加载的服务实例
     */
    public static synchronized <S> S load(String serviceName, Class<S> serviceClass) {
        return StreamSupport
                .stream(ServiceLoader.load(serviceClass).spliterator(), false)
                .peek(service -> put(serviceName, service))
                .findFirst()
                .orElseThrow(() -> new RuntimeException("no service instance"));
    }

    /**
     * 加载所有服务实例
     *
     * @param serviceClass 待加载服务实例的字节码对象
     * @param <S>          待加载服务实例的类型
     * @return 加载的服务实例的集合
     */
    public static synchronized <S> Collection<S> loadAll(Class<S> serviceClass) {
        return loadAll(serviceClass.getCanonicalName(), serviceClass);
    }

    /**
     * 加载所有服务实例
     *
     * @param serviceName  待加载服务实例的名称
     * @param serviceClass 待加载服务实例的字节码对象
     * @param <S>          待加载服务实例的类型
     * @return 加载的服务实例的集合
     */
    public static synchronized <S> Collection<S> loadAll(String serviceName, Class<S> serviceClass) {
        return StreamSupport.
                stream(ServiceLoader.load(serviceClass).spliterator(), false)
                .peek(service -> put(serviceName, service))
                .collect(Collectors.toList());
    }

    // 将服务实例放入映射中
    private static <S> void put(String serviceName, S service) {
        SERVICE_MAPPER
                .computeIfAbsent(serviceName, k -> new ArrayList<>())
                .add(service);
    }
}
